#!/usr/bin/env python
# coding:utf-8
# created by Peixuan Shu
# 2023.4.7
"""
buttons and console frame
"""

import tkinter as tk
# from tkinter import ttk
# import tkinter.messagebox 
from tkinter import filedialog
# import matplotlib.pyplot as plt

# import rospy
from std_msgs.msg import Int32

import os

green = '#95C943'
font = 'TkDefaultFont'+' ' # Times

class UAVbutton(tk.Button):
    """ Styled and configured UAV Button  """

    def __init__(self, master, text, **kwargs):
        self.master = master
        self.scale = master.scale
        super().__init__(master, font=font+'12', fg=green, text=text)
        self.config(**kwargs)
        ### right click menu
        self.right_click_menu = tk.Menu(self)
        # self.menu1.add_cascade(label = 'folder', menu=self.menu)
        ### right click
        self.bind('<Button-3>', self.pop_menu)
        ### enter
        self.bind('<Enter>', lambda x: self.cursor_hover(True))
        ### leave
        self.bind('<Leave>', lambda x: self.cursor_hover(False))

    def cursor_hover(self, hovering):
        if hovering:
            self.config(fg='black')
        else:
            self.config(fg=green)
    
    def pop_menu(self, event):
        """ pop out menu"""
        self.right_click_menu.post(event.x_root, event.y_root) 



class TopFrame(tk.Frame):
    """
    Top fram contains the menu to add URI's and choose which test to run
    """

    def __init__(self, root, main_gui):
        self.scale = root.scale_factor
        super().__init__(root, width=int(1200 * self.scale), height=int(650 * self.scale), bd=2, relief='ridge')

        self.main_gui = main_gui
        self.pack()
        self.grid_propagate(0) # 0

        self.add_id = 0
        self.allselect = tk.StringVar()
        self.allselect.set("no")

        self.show_buttons()

    def show_buttons(self):
        """ Buttons for controlling the URI listbox """
        padx = int(5*self.scale)
        pady = int(5*self.scale)
        row_num = 5
        col_num = 6
        self.uav_button = {}
        self.battery_label = {}
        self.pose_src_label = {}
        for i in range(row_num):
            for j in range(col_num):
                id = i*col_num+j+1
                ### uav button
                button = UAVbutton(self, str(id), width=5)
                button.grid(row=2*i, column=2*j, columnspan=2, padx=padx, pady=pady)
                self.uav_button[id] = button
                callbacks = self._button_callbacks(id)
                # left click uav button callback
                self.uav_button[id].configure(command=callbacks[0]) # left click to connect to uav
                # right click uav button callback
                self.uav_button[id].right_click_menu.add_command(label='takeoff', command=callbacks[1])
                self.uav_button[id].right_click_menu.add_command(label='land', command=callbacks[2])
                self.uav_button[id].right_click_menu.add_command(label='cancle')
                ### uav battery percentage label
                label = tk.Label(self, font=font+'10', fg=green, text='')
                label.grid(row=2*i+1, column=j*2)
                self.battery_label[id] = label
                ### pose source label
                label = tk.Label(self, font=font+'10', fg=green, text='')
                label.grid(row=2*i+1, column=j*2+1)
                self.pose_src_label[id] = label
        
        # init number entry
        self.init_num_entry = tk.Entry(self, font='Coself.urier 12', width=7, bd=2, relief='ridge',
                            highlightthickness=0, justify="center")
        self.init_num_entry.grid(row=0, column=col_num*2+1, padx=padx, pady=pady)
        self.init_num_entry.insert(0, '1')
        self.init_num_entry.focus_set()      
        # init_label = tk.Label(self, font=font+'10', fg=green, text='from')
        # init_label.grid(row=1, column=col_num+1)

        ### init number label
        from_label = tk.Label(self, font=font+'10', fg="grey", text='from')
        from_label.grid(row=1, column=col_num*2+1)

        # end number entry
        self.end_num_entry = tk.Entry(self, font='Coself.urier 12', width=7, bd=2, relief='ridge',
                            highlightthickness=0, justify="center")
        self.end_num_entry.grid(row=2, column=col_num*2+1, padx=padx, pady=pady)
        self.end_num_entry.insert(0, '30')
        self.end_num_entry.focus_set()   
        # end_label = tk.Label(self, font=font+'10', fg=green, text='to')
        # end_label.grid(row=3, column=col_num+1)

        ### end number label
        to_label = tk.Label(self, font=font+'10', fg="grey", text='to')
        to_label.grid(row=3, column=col_num*2+1)

        # select button
        self.select_button = tk.Button(self, font=font+'12', text="select", fg="grey", width=5, command=self._cb_select)
        self.select_button.grid(row=4, column=col_num*2+1, padx=padx, pady=pady)

        # command number entry
        self.cmd_num_entry = tk.Entry(self, font='Coself.urier 12', width=7, bd=2, relief='ridge',
                            highlightthickness=0, justify="center")
        self.cmd_num_entry.grid(row=6, column=col_num*2+1, padx=padx, pady=pady)
        self.cmd_num_entry.insert(0, '2')
        self.cmd_num_entry.focus_set()   

        ### command number label
        command_label = tk.Label(self, font=font+'10', fg="grey", text='/command')
        command_label.grid(row=7, column=col_num*2+1)

        # send command button
        self.send_button = tk.Button(self, font=font+'12', text="send", bg="grey", fg="white", width=5, command=self._cb_send_command)
        self.send_button.grid(row=8, column=col_num*2+1, padx=padx, pady=pady)

        # config file path entry
        self.config_entry = tk.Entry(self, font='Coself.urier 12', bd=2, relief='ridge',
                            highlightthickness=0)
        self.config_entry.grid(row=row_num*2, column=0, columnspan=4*2, padx=padx, pady=pady, sticky="WE")
        self.config_entry.insert(0, self.main_gui.tello_config)
        self.config_entry.focus_set()   

        # browse button
        self.browse_button = tk.Button(self, font=font+'12', width=5, fg="grey", text="browse", command=self._cb_update_config)
        self.browse_button.grid(row=row_num*2, column=4*2, columnspan=2, padx=padx, pady=pady)

        # take off button
        self.takeoff_button = tk.Button(self, font=font+'12', width=5, bg="grey", fg="white", text="takeoff", command=self._cb_takeoff)
        self.takeoff_button.grid(row=row_num*2, column=(4+1)*2, columnspan=2, padx=padx, pady=pady)

        # land button
        self.land_button = tk.Button(self, font=font+'12', width=5, bg="grey", fg="white", text="land", command=self._cb_land)
        self.land_button.grid(row=row_num*2, column=(4+2)*2, columnspan=2, padx=padx, pady=pady)


    def show_status(self, id, battery_perct, pose_source_ok, pose_source):
        # display battery perctentage
        self.battery_label[id].configure(text="{}%".format(battery_perct)) 
        if battery_perct > 20:
            self.battery_label[id].configure(fg=green) 
        else:
            self.battery_label[id].configure(fg="red") 
        # display pose source status
        if pose_source_ok == True:
            self.pose_src_label[id].configure(text=pose_source, fg=green)
        else:
            self.pose_src_label[id].configure(text=pose_source, fg="grey")

    ###### Callback from buttons #########
    def _cb_select(self):
        """ select connecting uavs """
        init_num = int(self.init_num_entry.get())
        end_num = int(self.end_num_entry.get())
        if (init_num <1):
            self.main_gui.print_console("[Invalid] init number should be larger than 1!", color = "red")
        elif (end_num >30):
            self.main_gui.print_console("[Invalid] end number should be smaller than 30!", color = "red")
        elif (end_num < init_num):
            self.main_gui.print_console("[Invalid] end number should be larger than start number!", color = "red")
        else:
            for id in range(init_num, end_num+1):
                self._connect(id)
    
    def _cb_send_command(self):
        """ send number to /command topic """
        msg = Int32()
        msg.data = int(self.cmd_num_entry.get())
        self.main_gui.cmd_pub.publish(msg)
        self.main_gui.print_console("[Send] send {} to /command".format(msg.data))        

    def _cb_takeoff(self):
        """send takeoff command 1"""
        msg = Int32()
        msg.data = 1
        self.main_gui.cmd_pub.publish(msg)
        self.main_gui.print_console("[Takeoff] send {} to /command".format(msg.data))

    def _cb_land(self):
        """send land command 9"""
        msg = Int32()
        msg.data = 9
        self.main_gui.cmd_pub.publish(msg)
        self.main_gui.print_console("[Land] send {} to /command".format(msg.data))

    def _cb_update_config(self):
        """ browse and update config file path """
        folder = os.path.dirname(os.path.abspath(__file__)) # absolute path of this folder 
        default_dir = os.path.dirname(os.path.dirname(folder)) + "/config"
        config_path = filedialog.askopenfilename(title='choose tello config yaml',initialdir=default_dir)
        self.main_gui.update_tello_config(config_path)

    def _connect(self, id):
        """ connect uav and check if connected """
        self.uav_button[id].configure(bg="yellow") # change button to yellow color
        self.main_gui.connect(id) # init and connect to uav
    
    def _takeoff_single(self, id):
        """ takeoff single uav """
        msg = Int32()
        msg.data = id
        self.main_gui.takeoff_num_pub.publish(msg)
        self.main_gui.print_console("[Send] send {} to /takeoff_num".format(msg.data))

    def _land_single(self, id):
        """ land single uav """
        msg = Int32()
        msg.data = id
        self.main_gui.land_num_pub.publish(msg)
        self.main_gui.print_console("[Send] send {} to /land_num".format(msg.data))


    def _button_callbacks(self, id):
        if id==1:
            return lambda: self._connect(1), lambda: self._takeoff_single(1), lambda: self._land_single(1)
        elif id==2:
            return lambda: self._connect(2), lambda: self._takeoff_single(2), lambda: self._land_single(2)
        elif id==3:
            return lambda: self._connect(3), lambda: self._takeoff_single(3), lambda: self._land_single(3)
        elif id==4:
            return lambda: self._connect(4), lambda: self._takeoff_single(4), lambda: self._land_single(4)
        elif id==5:
            return lambda: self._connect(5), lambda: self._takeoff_single(5), lambda: self._land_single(5)
        elif id==6:
            return lambda: self._connect(6), lambda: self._takeoff_single(6), lambda: self._land_single(6)
        elif id==7:
            return lambda: self._connect(7), lambda: self._takeoff_single(7), lambda: self._land_single(7)
        elif id==8:
            return lambda: self._connect(8), lambda: self._takeoff_single(8), lambda: self._land_single(8)
        elif id==9:
            return lambda: self._connect(9), lambda: self._takeoff_single(9), lambda: self._land_single(9)
        elif id==10:
            return lambda: self._connect(10), lambda: self._takeoff_single(10), lambda: self._land_single(10)
        elif id==11:
            return lambda: self._connect(11), lambda: self._takeoff_single(11), lambda: self._land_single(11)
        elif id==12:
            return lambda: self._connect(12), lambda: self._takeoff_single(12), lambda: self._land_single(12)
        elif id==13:
            return lambda: self._connect(13), lambda: self._takeoff_single(13), lambda: self._land_single(13)
        elif id==14:
            return lambda: self._connect(14), lambda: self._takeoff_single(14), lambda: self._land_single(14)
        elif id==15:
            return lambda: self._connect(15), lambda: self._takeoff_single(15), lambda: self._land_single(15)
        elif id==16:
            return lambda: self._connect(16), lambda: self._takeoff_single(16), lambda: self._land_single(16)
        elif id==17:
            return lambda: self._connect(17), lambda: self._takeoff_single(17), lambda: self._land_single(17)
        elif id==18:
            return lambda: self._connect(18), lambda: self._takeoff_single(18), lambda: self._land_single(18)
        elif id==19:
            return lambda: self._connect(19), lambda: self._takeoff_single(19), lambda: self._land_single(19)
        elif id==20:
            return lambda: self._connect(20), lambda: self._takeoff_single(20), lambda: self._land_single(20)
        elif id==21:
            return lambda: self._connect(21), lambda: self._takeoff_single(21), lambda: self._land_single(21)
        elif id==22:
            return lambda: self._connect(22), lambda: self._takeoff_single(22), lambda: self._land_single(22)
        elif id==23:
            return lambda: self._connect(23), lambda: self._takeoff_single(23), lambda: self._land_single(23)
        elif id==24:
            return lambda: self._connect(24), lambda: self._takeoff_single(24), lambda: self._land_single(24)
        elif id==25:
            return lambda: self._connect(25), lambda: self._takeoff_single(25), lambda: self._land_single(25)
        elif id==26:
            return lambda: self._connect(26), lambda: self._takeoff_single(26), lambda: self._land_single(26)
        elif id==27:
            return lambda: self._connect(27), lambda: self._takeoff_single(27), lambda: self._land_single(27)
        elif id==28:
            return lambda: self._connect(28), lambda: self._takeoff_single(28), lambda: self._land_single(28)
        elif id==29:
            return lambda: self._connect(29), lambda: self._takeoff_single(29), lambda: self._land_single(29)
        elif id==30:
            return lambda: self._connect(30), lambda: self._takeoff_single(30), lambda: self._land_single(30)
        else:
            return lambda: self.main_gui.print_console("uav id not supported")